#pragma once
#include <fcntl.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include "controlcan.h"

#define RECV_BUFFER_MAX 3000

namespace CANalyst2
{
    enum class CanBaud {
        BAUD_1M,
        BAUD_500K,
        BAUD_250K,
        BAUD_125K
    };

    struct CanStamped : public VCI_CAN_OBJ
    {
        uint64_t timestamp;
        CanStamped()
        {
            ID = 0;
            SendType = 0;
            RemoteFlag = 0;
            ExternFlag = 0;
            DataLen = 8;
            TimeStamp = 0;
            TimeFlag = 0;
            std::fill(std::begin(Data), std::end(Data), 0);
            std::fill(std::begin(Reserved), std::end(Reserved), 0);

            timestamp = 0;
        }
    };

    struct Canet_2e_u
    {
        struct
        {
            uint8_t can_length : 4;
            uint8_t reserved : 2;
            uint8_t rtr : 1; // must be zero
            uint8_t frame_format : 1;
        } can_info;
        uint8_t id[4];
        uint8_t data[8];
    };

    class CANStream
    {
    public:
        virtual ~CANStream() = default;
        virtual int open(CanBaud baud, int index) = 0;
        virtual int recv(std::vector<CanStamped> &buffer, int timeout_msec) = 0;
        virtual int send(std::vector<CanStamped> &buffer, int timeout_msec) = 0;
        virtual int recv(std::vector<CanStamped> &buffer, int timeout_msec, int index) = 0;
        virtual int send(std::vector<CanStamped> &buffer, int timeout_msec, int index) = 0;
        virtual inline bool get_can_usb_opened() = 0;
    };

    class SocketCAN : public CANStream
    {
    private:
        int skt;
        struct ifreq ifr;

        struct msghdr msg;
        struct iovec iov;
        char ctrlmsg[CMSG_SPACE(sizeof(struct timeval) + 3 * sizeof(struct timespec) + sizeof(__u32))];
        struct can_frame frame;
        struct sockaddr_can addr;

    public:
        int open(CanBaud baud, int index) {return 0;};
        int recv(std::vector<CanStamped> &buffer, int timeout_msec) {return 0;};
        int send(std::vector<CanStamped> &buffer, int timeout_msec) {return 0;};
        int recv(std::vector<CanStamped> &buffer, int timeout_msec, int index) {return 0;};
        int send(std::vector<CanStamped> &buffer, int timeout_msec, int index) {return 0;};
        inline bool get_can_usb_opened() { return false; }
    };

    class UsbCAN : public CANStream
    {
    private:
        VCI_CAN_OBJ recv_buffer[RECV_BUFFER_MAX];
        bool is_can_usb_opened = false;

    public:
        UsbCAN();
        virtual ~UsbCAN();

        int open(CanBaud baud, int index);
        int recv(std::vector<CanStamped> &buffer, int timeout_msec, int index);
        int send(std::vector<CanStamped> &buffer, int timeout_msec, int index);
        int recv(std::vector<CanStamped> &buffer, int timeout_msec);
        int send(std::vector<CanStamped> &buffer, int timeout_msec);
        inline bool get_can_usb_opened() { return is_can_usb_opened; }
    };
}